package pl.wroc.uni.ii.evolution.engine.individuals;

import java.util.ArrayList;
import java.util.Arrays;

import pl.wroc.uni.ii.evolution.engine.prototype.EvIndividual;

/**
 * 
 * Class for binary individual with fixed length.<BR>
 * Each individual contains defined number of boolean values.
 * 
 * @author Piotr Baraniak, Tomasz Kozakiewicz
 */
public class EvBinaryVectorIndividual extends EvIndividual {

  private static final long serialVersionUID = -3883047179476641241L;

  // it's public because of optimalisation purposes.
  // If you are setting bits directly - remember that there is method
  // invalidate - which prevents from evaluating objective function many
  // times of the same individual.
  public boolean[] vector;

  /**
   * 
   * @param d
   *          fixed length of individual
   */
  public EvBinaryVectorIndividual(int d) {
    vector = new boolean[d];
  }

  /**
   * 
   * @param source_vector
   *          source vector for new individual
   */
  public EvBinaryVectorIndividual(boolean ... source_vector) {
    int vector_length = source_vector.length;
    vector = new boolean[vector_length];
    for (int i=0; i < vector_length; i++) {
      vector[i] = source_vector[i];
    }
  }

  /**
   * Gets binary value of i-th bit.
   * 
   * @param i
   *          bit position in chromosome
   * @return value of i-th bit
   */
  public boolean getBit(int i) {
    return vector[i];
  }

  /**
   * Sets bit value to binary value.
   * 
   * @param i
   *          bit position in chromosome
   * @param b
   *          binary value of bit
   */
  public void setBit(int i, boolean b) {
    this.invalidate();
    vector[i] = b;
  }

  /**
   * Gets individual dimension.
   * 
   * @return length of chromosome
   */
  public int getDimension() {
    return vector.length;
  }

  /*
   * (non-Javadoc)
   * 
   * @see java.lang.Object#toString()
   */
  public String toString() {
    StringBuffer output = new StringBuffer();
    for (int i = 0; i < vector.length; i++) {
      output.append(vector[i] ? "1" : "0");
    }
    return output.toString();
  }

  /**
   * String representation of individual containing only binary position
   * specified in Integer ArrayList.<BR>
   * 
   * @param positions
   * @return
   */
  public String toString(ArrayList<Integer> positions) {

    if (positions.size() > vector.length) {
      throw new IllegalArgumentException("Argument length to big");
    }

    StringBuffer output = new StringBuffer();
    for (int pos : positions) {
      if (pos > vector.length) {
        throw new IllegalArgumentException("One of ArrayList value "
            + "is bigger than size of the individual");
      }
      output.append(vector[pos] ? "1" : "0");
    }
    return output.toString();
  }

  public EvBinaryVectorIndividual clone() {
    EvBinaryVectorIndividual b1 = new EvBinaryVectorIndividual(vector);
    b1.setObjectiveFunction(getObjectiveFunction());
    if (this.isValidate()) {
      b1.uninvalidate(this.getObjectiveFunctionValue());
    }
    return b1;
  }

  /**
   * Set bits in positions given in Arraylist with corespondent 
   * boolean value given in boolean table
   * 
   * @param positions
   * @param values
   */
  public void setBits(ArrayList<Integer> positions, Boolean[] values) {
    if (positions.size() != values.length) {
      throw new IllegalArgumentException("setBits: argument length don't match");
    }

    int k = 0;
    int position_size = positions.size();
    for (int i=0; i < position_size; i++)  {
      vector[positions.get(i)] = values[k];
      k++;
    }
    invalidate();

  }

  /**
   * Get bits values with specified positions in ArrayList.<BR>
   * Returns bits in order corespondently to order of positions in AttayList.
   * 
   * @param positions
   * @param values
   */
  public Boolean[] getBits(ArrayList<Integer> positions) {
    Boolean[] bits = new Boolean[positions.size()];

    int k = 0;
    int position_size = positions.size();
    for (int i=0; i < position_size; i++)  {
      bits[k] = vector[positions.get(i)];
      k++;
    }
    return bits;
  }
  
  /**
   * Similat to getBit() but in this case the whole vector is returned
   * 
   * @return whole boolean table of bits of this individual
   */
  public boolean[] getBits() {
    return vector;
  }

  // bugged, insn't working 100% good 
  /*
  public int hashCode() {
    final int PRIME = 31;
    int result = 1;
    result = PRIME * result + Arrays.hashCode(vector);
    return result;
  }*/
  
  @Override
  public int hashCode() {
    return Arrays.hashCode(vector);
  }  

  @Override
  public boolean equals(Object obj) {
    if (obj == null)
      return false;
    if (getClass() != obj.getClass())
      return false;
    if (!Arrays.equals(vector, ((EvBinaryVectorIndividual) obj).vector))
      return false;
    return true;
  }

  /**
   * equals every pair of bit at the same position of current and given
   * BinaryIndividual and returns number of bits that are equal
   * 
   * @param individual -
   *          Individual to be compared
   * @return number of bits that are the same
   */
  public int countIdenticalBits(EvBinaryVectorIndividual individual) {
    if (individual.getDimension() != getDimension()) {
      throw new IllegalStateException("Number of bits must be the same");
    } else {
      int counter = 0;
      for (int i = 0; i < getDimension(); i++) {
        if (individual.getBit(i) == getBit(i)) {
          counter++;
        }
      }
      return counter;
    }
  }

}
